#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import time
from queue import LifoQueue
from maze import Maze
from socket import socket, AF_INET, SOCK_STREAM
from message import MessageCode


class Client:

    def __init__(self):
        self.client = socket(AF_INET, SOCK_STREAM)
        self.client.connect(('127.0.0.1', 20000))
        self.maze = Maze(10)
        self.current_position = [0, 0]

    def close(self):
        self.client.close()

    def recv(self):
        resp = self.client.recv(8192)
        return json.loads(resp.decode())

    def send(self, msg_num, msg_content, msg_desc=""):
        msg = {
            "num": msg_num,
            "content": msg_content,
            "desc": msg_desc
        }
        msg = json.dumps(msg).encode()
        self.client.send(msg)

    def bfs(self, start):
        result, _result = [], []
        queue = LifoQueue()
        queue.put(start)

        def loop():
            while not queue.empty():
                current = queue.get()
                result.append(current)
                current_prop = self.maze.matrix[current[0]][current[1]]
                for ap in self.maze.get_node_next_to(current):
                    prop = self.maze.matrix[ap[0]][ap[1]]
                    # 如果可走相邻点是可触发道具则停下，等待道具触发
                    if prop.typ in ["宝箱", "地雷", "炸弹", "手雷"]:
                        result.append(ap)
                        return result
                    # 如果可走相邻点有迷雾则停下，等待迷雾刷新
                    elif prop.typ in ["迷雾"]:
                        return result
                    # 将不是障碍和没走过的路点加入队列
                    elif prop.walked is False and prop.typ not in ["障碍"]:
                        current_prop.walked = True
                        queue.put(ap)

        def loop1():
            while not queue.empty():
                current = queue.get()
                prop = self.maze.matrix[current[0]][current[1]]
                if prop.typ in ["迷雾"]:
                    break
                result.append(current)
                if prop.typ in ["宝箱", "地雷", "炸弹", "手雷"]:
                    break
                adjacent_points = self.maze.get_node_next_to(current)
                for ap in adjacent_points:
                    prop = self.maze.matrix[ap[0]][ap[1]]
                    if prop.walked is False and prop.typ not in ["障碍"]:
                        prop.walked = True
                        queue.put(ap)

        loop()
        reverse_result = result[:]
        reverse_result.reverse()
        for node in reverse_result:
            if not _result:
                _result.append(node)
            elif node in self.maze.get_node_next_to(_result[-1]):
                _result.append(node)
        _result.reverse()
        return _result

    def way_finding(self):
        """
        根据当前迷宫和当前所在点寻找最近能前往的可触发点
        :return: way_point, list(tuple(x, y), ...)
        """
        return self.bfs(self.current_position)

    def run(self):

        def check():
            for row in self.maze.matrix:
                for node in row:
                    if node.typ in ["宝箱", "迷雾", "炸弹", "手雷", "地雷"]:
                        return False
            return True

        self.send(MessageCode.Hello, None)
        while True:
            msg = self.recv()
            if msg['num'] == MessageCode.Maze:
                print(msg['desc'])
                maze = msg['content']
                self.maze = self.maze.deserialize(maze)
                print(self.maze)
                if check():
                    self.send(MessageCode.Bye, None)
                    self.close()
                    exit(0)
                self.current_position = self.maze.current_node
                way_point = self.way_finding()
                print("Client生成路点: {}".format(way_point))
                self.send(MessageCode.WayPoint, way_point)
            elif msg['num'] == MessageCode.Error:
                print(msg['desc'])
                way_point = self.way_finding()
                print("Client生成路点: {}".format(way_point))
                self.send(MessageCode.WayPoint, way_point)
            time.sleep(1)

if __name__ == "__main__":
    c = Client()
    try:
        c.run()
    except Exception:
        c.close()
